/**
 * @file bsp_rtc.c
 * @brief RTC封装API
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2020-11-12
 * @copyright Copyright (c) 2020  烽火通信
 ***************************************************************
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version  <th>Author  <th>Description
 * <tr><td>2020-11-12 <td>1.0      <td>wangh   <td>内容
 * </table>
 */
#include "bsp_rtc.h"
#if USE_BSP_RTC
#include "bsp.h"
#include <stdbool.h>

#undef LOG_LEVEL_
#define LOG_LEVEL_  LOG_LVL_ASSERT
#include "bsp_log.h"


#define leapyear(year)      ((year) % 4 == 0)
#define days_in_year(a)     (leapyear(a) ? 366 : 365)
#define days_in_month(a)    (month_days[(a) - 1])

static uint8_t month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static bool RtcInitialized = false;

#define RTC_TIMEOUT_MAX        0x3FFFFFFU   ///< LSE起振超时

/**
 * @brief RTC初始化
 */
void RtcInit( void )
{
#ifdef STM32F10X
    uint32_t timeOut = RTC_TIMEOUT_MAX;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);    /* 使能PWR和BKP外设时钟 */
    PWR_BackupAccessCmd(ENABLE);    /* 使能后备寄存器访问 */

    /* 在启动时检查备份寄存器BKP_DR1,如果内容不是0x5050,则需重新配置RTC */
    if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)
    {
        BKP_DeInit();               /* 复位备份区域 */

        /* 批量产品的时候，很容易出现外部晶振不起振的情况,建议使用LSI */
#if (RTC_CLOCK_SOURCE == RTC_CLOCK_SOURCE_LSE)
        RCC_LSEConfig(RCC_LSE_ON);	/* 使能低速外部时钟 LSE */

        /* 等待 LSE ready */
        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
        {
            if( ( timeOut-- ) == 0 )
            {
                log_e( "LSE Ready timeout...\r\n" );
                return;
            }
        }  /* LSE起振超时处理 */
        
        RCC_RTCCLKConfig( RCC_RTCCLKSource_LSE );   /* 选择LSE作为RTC时钟 */
        RCC_RTCCLKCmd(ENABLE);      /* 使能RTC时钟 */
        RTC_WaitForSynchro();       /* 等待RTC寄存器与APB1同步 */
        RTC_WaitForLastTask();      /* 等待最近一次对RTC寄存器的写操作完成 */
        
        RTC_ITConfig(RTC_IT_SEC, ENABLE);   /* 使能RTC秒中断 */
        RTC_WaitForLastTask();

        // RTC_EnterConfigMode();
        RTC_SetPrescaler(32767);    /* 设置RTC预分频的值 */
        RTC_WaitForLastTask();
        // RTC_ExitConfigMode();       /* 退出配置模式 */
        RTC_WaitForLastTask();
#else
        RCC_LSICmd(ENABLE);
        while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
        {
            if( ( timeOut-- ) == 0 )
                return;
        }  /* 等待 LSI 准备好 */

        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
        RCC_RTCCLKCmd(ENABLE);
        RTC_WaitForSynchro();   /* 等待RTC寄存器同步，因为RTC时钟是低速的，内环时钟是高速的，所以要同步 */
        RTC_WaitForLastTask();

        RTC_ITConfig(RTC_IT_SEC, ENABLE);
        RTC_WaitForLastTask();
        /* 设置 RTC 分频: 使 RTC 周期为1s ,LSI约为40KHz */
        RTC_SetPrescaler(40000-1);
        RTC_WaitForLastTask();
#endif
        BKP_WriteBackupRegister(BKP_DR1, 0X5050);
    }
    else    /* 无需设置新时钟 */
    {
#if (RTC_CLOCK_SOURCE == RTC_CLOCK_SOURCE_LSI)
        RCC_LSICmd(ENABLE);
        while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
        {
            if( ( timeOut-- ) == 0 )
                return;
        }
#endif

        /* 重启原因 */
        if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
            log_i("Power On Reset occurred...\r\n");
        if (RCC_GetFlagStatus(RCC_FLAG_SFTRST) != RESET)
            log_i("Soft Reset occurred...\r\n");
        if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
            log_i("Pin Reset occurred...\r\n");
        if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
            log_i("IWDG Reset occurred...\r\n");
        if (RCC_GetFlagStatus(RCC_FLAG_LPWRRST) != RESET)
            log_i("low-power Reset occurred...\r\n");
        
        RCC_ClearFlag();    /* 清除复位标志 */

        log_d("No need to configure RTC...\r\n");
        
        RTC_WaitForSynchro();   /* 等待寄存器同步 */
        RTC_ITConfig(RTC_IT_SEC, ENABLE);   /* 使能RTC秒中断 */
        RTC_WaitForLastTask();  /* 等待上次RTC寄存器写操作完成 */
    }

    // NVIC_InitTypeDef NVIC_InitStructure;
    // NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 14;
    // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    // NVIC_Init(&NVIC_InitStructure);

#elif defined GD32F10X
    rcu_periph_clock_enable( RCU_PMU );
    rcu_periph_clock_enable( RCU_BKPI );
    pmu_backup_write_enable();

    /* 在启动时检查备份寄存器BKP_DR1,如果内容不是0x5050,则需重新配置RTC */
    if (bkp_data_read(BKP_DATA_0) != 0x5050)
    {
        bkp_deinit();               /* 复位备份区域 */

        /* 批量产品的时候，很容易出现外部晶振不起振的情况,建议使用LSI */
#if (RTC_CLOCK_SOURCE == RTC_CLOCK_SOURCE_LSE)
        rcu_osci_on(RCU_LXTAL);			/* 使能低速外部时钟 LSE */
        rcu_osci_stab_wait(RCU_LXTAL);	/* 等待 LSE ready */
        rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);   /* 选择LSE作为RTC时钟 */
        rcu_periph_clock_enable(RCU_RTC);	/* 使能RTC时钟 */
        rtc_register_sync_wait();		/* 等待RTC寄存器与APB1同步 */
        rtc_lwoff_wait();				/* 等待最近一次对RTC寄存器的写操作完成 */	
        
        // rtc_interrupt_enable(RTC_INT_SECOND);	/* 使能RTC秒中断 */
        // rtc_lwoff_wait();

        rtc_prescaler_set(32767);
        rtc_lwoff_wait();
#else
        rcu_osci_on(RCU_IRC40K);
        rcu_osci_stab_wait(RCU_IRC40K);
        rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);
        rcu_periph_clock_enable(RCU_RTC);
        rtc_register_sync_wait();
        rtc_lwoff_wait();
        
        // rtc_interrupt_enable(RTC_INT_SECOND);    /* 使能RTC秒中断 */
        // rtc_lwoff_wait();
        
        rtc_prescaler_set(40000-1);
        rtc_lwoff_wait();
#endif
        bkp_data_write(BKP_DATA_0, 0x5050);
    }
    else    /* 无需设置新时钟 */
    {
#if (RTC_CLOCK_SOURCE == RTC_CLOCK_SOURCE_LSI)
        rcu_osci_on(RCU_IRC40K);
        rcu_osci_stab_wait(RCU_IRC40K);
#endif

        /* 重启原因 */
        if (rcu_flag_get(RCU_FLAG_PORRST) != RESET)
            log_i("Power On Reset occurred...\r\n");
        if (rcu_flag_get(RCU_FLAG_SWRST) != RESET)
            log_i("Soft Reset occurred...\r\n");
        if (rcu_flag_get(RCU_FLAG_EPRST) != RESET)
            log_i("Pin Reset occurred...\r\n");
        if (rcu_flag_get(RCU_FLAG_FWDGTRST) != RESET)
            log_i("FWDGT Reset occurred...\r\n");
        if (rcu_flag_get(RCU_FLAG_LPRST) != RESET)
            log_i("low-power Reset occurred...\r\n");

        rcu_all_reset_flag_clear(); /* 清除复位标志 */

        log_d("No need to configure RTC...\r\n");
        
        rtc_register_sync_wait();   /* 等待寄存器同步 */
    //         rtc_interrupt_enable(RTC_INT_SECOND);    /* 使能RTC秒中断 */
        rtc_lwoff_wait();           /* 等待上次RTC寄存器写操作完成 */
    }

    // nvic_irq_enable( RTC_IRQn, 1, 0 );
#endif

    RtcInitialized = true;
}

#if RTC_CAL_WEEK
/**
 * @brief 计算公历
 * @param  tm          时间结构体
 */
static void GregorianDay( rtc_time_t *tm )
{
    int leapsToDate;
    int lastYear;
    int day;
    int MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };

    lastYear=tm->tm_year-1;

    /*计算从公元元年到计数的前一年之中一共经历了多少个闰年*/
    leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;      

    /*如若计数的这一年为闰年，且计数的月份在2月之后，则日数加1，否则不加1*/
    if((tm->tm_year%4==0) &&
        ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
        (tm->tm_mon>2)) 
    {
        day=1;
    }
    else 
        day=0;

    /*计算从公元元年元旦到计数日期一共有多少天*/
    day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday;

    tm->tm_week = day%7;
}
#endif

/**
 * @brief 转换tm为秒数 \n
 * 参考 mktime 函数
 * @warning 转换时间起始 1970-01-01 00:00:00，输入参数应大于这个时间
 * @param  tm          时间结构体
 * @return uint32_t 秒数
 */
uint32_t my_mktime( rtc_time_t *tm )
{
    // if (0 >= (int) (tm->tm_mon -= 2)) { /* 1..12 -> 11,12,1..10 */
    //     tm->tm_mon += 12;   /* Puts Feb last since it has leap day */
    //     tm->tm_year -= 1;
    // }

    if (tm->tm_mon > 2)
    {
        tm->tm_mon -= 2;
    }
    else
    {
        tm->tm_mon += 10;
        tm->tm_year -= 1;
    }

    return (((
        (uint32_t) (tm->tm_year/4 - tm->tm_year/100 + tm->tm_year/400 + 367*tm->tm_mon/12 + tm->tm_mday) +
            tm->tm_year*365 - 719499
        )*24 + tm->tm_hour  /* now have hours */
        )*60 + tm->tm_min   /* now have minutes */
    )*60 + tm->tm_sec;      /* finally seconds */
}

/**
 * @brief 转换秒数为tm
 * @param  tim      秒数
 * @param  tm       RTC时间结构体
 */
void to_tm( uint32_t tim, rtc_time_t * tm )
{
    register uint32_t i;
    register long hms, day;

    day = tim / 86400;      /* 有多少天 */
    hms = tim % 86400;      /* 今天的时间，单位s */

    /* 计算 Hours, minutes, seconds */
    tm->tm_hour = hms / 3600;
    tm->tm_min = (hms % 3600) / 60;
    tm->tm_sec = (hms % 3600) % 60;

    /* 算出当前年份，起始的计数年份为1970年 */
    for (i = 1970; day >= days_in_year(i); i++)
    {
        day -= days_in_year(i);
    }
    tm->tm_year = i;

    /* 计算当前的月份 */
    if (leapyear(tm->tm_year)) {
        days_in_month(2) = 29;
    }
    for (i = 1; day >= days_in_month(i); i++) {
        day -= days_in_month(i);
    }
    days_in_month(2) = 28;
    tm->tm_mon = i;

    tm->tm_mday = day + 1;

#if RTC_CAL_WEEK
    GregorianDay(tm);   /* 计算星期 */
#endif
}

/**
 * @brief RTC设置时间
 * @param[in]  tm           RTC时间结构体
 * @return int8_t 
 * - @b Success : BSP_SUCCESS \n
 * - @b Fail
 * - @ref BSP_ERROR_NO_INIT - RTC没有初始化
 * - @ref BSP_ERROR_INVALID_PARAM - 非法入参
 */
int8_t RtcSet( rtc_time_t *tm )
{
    if( !RtcInitialized )
    {
        log_e( "RTC error: has no Initialized\r\n" );
        return BSP_ERROR_NO_INIT;
    }

    if (tm == NULL)
        return BSP_ERROR_INVALID_PARAM;

    if ( (tm->tm_year < 1970) || (tm->tm_mon > 12) || (tm->tm_mday > 31) || (tm->tm_hour > 24) || (tm->tm_min > 60) || (tm->tm_sec > 60) )
        return BSP_ERROR_INVALID_PARAM;

#ifdef STM32F10X
    RTC_SetCounter( my_mktime(tm) - TIME_ZOOM );
    RTC_WaitForLastTask();
#elif defined GD32F10X
    rtc_lwoff_wait();
    rtc_counter_set( my_mktime(tm) - TIME_ZOOM );
    rtc_lwoff_wait();
#endif

    return BSP_SUCCESS;
}

/**
 * @brief RTC获取时间
 * @param[out] tm           RTC时间结构体
 * @return int8_t 
 * - @b Success : BSP_SUCCESS \n
 * - @b Fail
 * - @ref BSP_ERROR_NO_INIT - RTC没有初始化
 * - @ref BSP_ERROR_INVALID_PARAM - 非法入参
 */
int8_t RtcGet( rtc_time_t *tm )
{
    if( !RtcInitialized )
    {
        log_e( "RTC error: has no Initialized\r\n" );
        return BSP_ERROR_NO_INIT;
    }
    
    if (tm == NULL)
        return BSP_ERROR_INVALID_PARAM;

    /*  把标准时间转换为北京时间*/
#ifdef STM32F10X
    uint32_t BJ_TimeVar = RTC_GetCounter() + TIME_ZOOM;
#elif defined GD32F10X
    uint32_t BJ_TimeVar = rtc_counter_get() + TIME_ZOOM;
#endif

    to_tm( BJ_TimeVar, tm );

    return BSP_SUCCESS; 
}


#if 0
/**
 * @brief 判断是不是闰年 \n
 * 月份   1  2  3  4  5  6  7  8  9  10 11 12 \n
 * 闰年   31 29 31 30 31 30 31 31 30 31 30 31 \n
 * 非闰年 31 28 31 30 31 30 31 31 30 31 30 31
 * @param  year        年份
 * @return uint8_t 1：是，0：不是
 */
static uint8_t Is_Leap_Year(uint16_t year)
{
    if (year % 4 == 0)      /* 必须能被4整除 */
    {
        if (year % 100 == 0)
        {
            if (year % 400 == 0)
                return 1;   /* 如果以00结尾,还要能被400整除 */
            else
                return 0;
        }
        else
            return 1;
    }
    else
        return 0;
}
#endif

#if 0
void RTC_IRQHandler(void)
{
    if (RTC_GetITStatus(RTC_IT_SEC) != RESET)   /* 秒钟中断 */
    {
    }
    if (RTC_GetITStatus(RTC_IT_ALR) != RESET)   /* 闹钟中断 */
    {
        RTC_ClearITPendingBit(RTC_IT_ALR);      /* 清闹钟中断 */
        log_d("RTC Alarm...\r\n");
    }

    RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW);  /* 清中断 */
    RTC_WaitForLastTask();
}
#endif

#endif	/* USE_BSP_RTC */
/*********** (C) COPYRIGHT 2020 FiberHome *****END OF FILE****/
